/**
 * 
 */
package spitfire.ksim.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;

import spitfire.ksim.algorithm.TimeWindowAlgorithm;
import spitfire.ksim.network.KNetwork;
import spitfire.ksim.network.KNetworkModule;
import spitfire.ksim.node.KGateway;
import spitfire.ksim.node.KNode;
import spitfire.ksim.node.KNodeNetworkModule;
import spitfire.ksim.node.KSensor;
import spitfire.ksim.node.KSensorNode;
import spitfire.ksim.simulation.KSimulation;
import spitfire.ksim.simulator.KSimTimeElapseListener;
import spitfire.ksim.simulator.KStateChangeListener;
import spitfire.ksim.util.ExtFilter;
import spitfire.ksim.util.TPoint;

/**
 * @author Adam
 * 
 */
public class KNetworkView extends JPanel implements KStateChangeListener {

	private static final long serialVersionUID = -5818200520079104072L;
	public static final Color SNODE_FILL_COLOR = Color.GRAY;
	public static final Color GTW_FILL_COLOR = Color.BLUE;
	public static final Color NODE_TEXT_COLOR = Color.decode("#680000");
	public static final int NODE_WIDTH = 30;
	public static final int NODE_HEIGHT = 30;
	public static final Font NODE_FONT = new Font("Arial", Font.BOLD, 16);

	private KSimulation sim;
	private KSimulationView simView;
	private KNode clickNode;
	private String deployNodeType;
	private String deployingNodePath;
	private boolean deployingNode, draggingNode;
	private int xM_Old, yM_Old;
//	, currentX, currentY;
//	private Map<String, JRadioButtonMenuItem> sensorTypeBtnMap = new HashMap<String, JRadioButtonMenuItem>();

	private JPopupMenu popNetwork = new JPopupMenu();
	private JMenuItem[] popSep = new JMenuItem[4];
	private JMenuItem popNetUtility = new JMenuItem("Network utilities:");
	private JMenuItem popNewSensor = new JMenuItem("   New node");
	private JMenuItem popNewGateway = new JMenuItem("   New gateway");
	private JMenuItem popNewScene = new JMenuItem("   Generate scenario");
	private JMenuItem popLoadScene = new JMenuItem("   Load scenario");
	private JMenuItem popSaveScene = new JMenuItem("   Save scenario");
	
	private JPopupMenu popSensor = new JPopupMenu();
	private JMenuItem popSensorUtility = new JMenuItem("Sensor utilities:");
	private JMenuItem popChangeSD = new JMenuItem("   Change semantic description of all sensors");
	private JMenuItem popClearSD = new JMenuItem("   Reset semantic description of all sensors");
	private JMenuItem popViewData = new JMenuItem("   View info");
	private JMenuItem popDelSen = new JMenuItem("   Delete this sensor");
	//by Danilo Test
	private JMenuItem popViewTimeWindow = new JMenuItem("   View TimeWindow");
	private JMenu popTimeWindowOptions = new JMenu("   TimeWindow Options");
		private JMenuItem popCommonTimeWindow = new JMenuItem("   Use common TimeWindow");
		private JMenu popOwnTimeWindow = new JMenu("   Use own TimeWindow");
			private JMenuItem popManuallyTimeWindow = new JMenuItem("   Manual TW");
			private JMenuItem popComputedTimeWindow = new JMenuItem("   Computed TW");
			
	private KToolFrame tunersFrame; 		
	
	private JPopupMenu popGateway = new JPopupMenu();
	private JMenuItem popGatewayUtility = new JMenuItem("Gateway utilities:");
	private JMenuItem popViewRuleBase = new JMenuItem("   View Rule base");
	private JMenuItem popDelGat = new JMenuItem("   Delete this gateway");

	public KNetworkView(KSimulationView simView) {
		super();
		setSimView(simView);
		setBackground(Color.WHITE);
		setPreferredSize(new Dimension(680, 500));
		
		clickNode = null;
		deployNodeType = null;
		deployingNode = false;
		draggingNode = false;
		// Network popup menu
		popNetwork.add(popNetUtility);
		popNetwork.add(popNewSensor);
		popNetwork.add(popNewGateway);
		popSep[0] = new JMenuItem("   --------");
		popSep[0].setEnabled(false);
		popNetwork.add(popSep[0]);
		popNetwork.add(popNewScene);
		popNetwork.add(popLoadScene);
		popNetwork.add(popSaveScene);
		
		// Sensor popup menu
		popSensor.add(popSensorUtility);
		popSensor.add(popChangeSD);
		popSensor.add(popClearSD);
		//by Danilo 
		//TimeWindow options
		popSensor.add(popTimeWindowOptions);
		popTimeWindowOptions.add(popCommonTimeWindow);
		popTimeWindowOptions.add(popOwnTimeWindow);
		popOwnTimeWindow.add(popManuallyTimeWindow);
		popOwnTimeWindow.add(popComputedTimeWindow);
		popSep[3] = new JMenuItem("   --------");
		popSep[3].setEnabled(false);
		popTimeWindowOptions.add(popSep[3]);
		popTimeWindowOptions.add(popViewTimeWindow);
		//##
		popSensor.add(popViewData);
		popSep[1] = new JMenuItem("   --------");
		popSep[1].setEnabled(false);
		popSensor.add(popSep[1]);
		popSensor.add(popDelSen);
		// Gateway popup menu
		popGateway.add(popGatewayUtility);
		popGateway.add(popViewRuleBase);
		popSep[1] = new JMenuItem("   --------");
		popSep[1].setEnabled(false);
		popGateway.add(popSep[1]);
		popGateway.add(popDelGat);
//		algoMniGroup.add(mnSingleAbsolute);
//		algoMniGroup.setSelected(mniAccumulative.getModel(), true);
		setVisible(true);
		
		//Mouse events for the netView
		addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent evt) { netViewMousePressed(evt); }
			public void mouseReleased(MouseEvent evt) { netViewMouseReleased(evt); }
			public void mouseClicked(MouseEvent evt) { netViewMouseClicked(evt); }
		});
		
		addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseDragged(MouseEvent evt) { netViewMouseDragged(evt); }
			public void mouseMoved(MouseEvent evt) { netViewMouseMoved(evt); }
		});
		
		//Popup menu listener
    	popNewSensor.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popNewSensorClick(evt); } });
		popNewGateway.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popNewGatewayClick(evt); } });
		popNewScene.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popNewSceneClick(evt); } });
		popLoadScene.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popLoadSceneClick(evt); } });
		popSaveScene.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popSaveSceneClick(evt); } });
			
		popChangeSD.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popChangeSDClick(evt); } });
		popClearSD.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popClearSDClick(evt); } });
		popViewData.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popViewDataClick(evt); } });
		
		//by Danilo TimeWindow Options
		popCommonTimeWindow.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt){ popCommonTimeWindowClick(evt); } });
		popManuallyTimeWindow.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){ popManuallyTimeWindowClick(evt); } });
		popComputedTimeWindow.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){ popComputedTimeWindowClick(evt); } });
		popViewTimeWindow.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popViewTimeWindowClick(evt); } });
		//
		popViewRuleBase.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {popViewRuleBaseClick(e); } });
		popDelSen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popDelNodeClick(evt); } });
		popDelGat.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) { popDelNodeClick(evt); } });
		
//		mniRepresentative.addActionListener(new ActionListener() {
//			@Override
//			public void actionPerformed(ActionEvent e) {
//				if (clickNode instanceof KGateway) {
//					((KGateway)clickNode).setRequestAlgorithm("KNN-1:Representative");
//				}
//			}
//		});
//		
//		mniAccumulative.addActionListener(new ActionListener() {
//			@Override
//			public void actionPerformed(ActionEvent e) {
//				if (clickNode instanceof KGateway) {
//					((KGateway)clickNode).setRequestAlgorithm("Accumulative");
//				}
//			}
//		});
	}

	public void netViewMouseClicked(MouseEvent evt) { 
		if (evt.getButton() == MouseEvent.BUTTON3) {
			clickNode = getNodeAt(evt.getX(), evt.getY());
			if (clickNode != null) {
				if (clickNode instanceof KSensorNode) {
					popSensor.show(evt.getComponent(), evt.getX(), evt.getY());
				} else if (clickNode instanceof KGateway) {
//					updateGatewayAlgoMenu();
					popGateway.show(evt.getComponent(), evt.getX(), evt.getY());
				}
			} else {
				popNetwork.show(evt.getComponent(), evt.getX(), evt.getY());
	   		}
	   	}
	}

//	private void updateGatewayAlgoMenu() {
//		String algo = ((KGateway)clickNode).getRequestAlgorithm();
//		if (algo.contains("Representative")) {
//			mniRepresentative.setSelected(true);
//		} else if (algo.contains("Accumulative")) {
//			mniAccumulative.setSelected(true);
//		} else if (algo.contains("SingleSensorAbsolute")) {
//			int idx = algo.indexOf("SingleSensorAbsolute-");
//			String type = algo.substring(idx+"SingleSensorAbsolute-".length());
//			sensorTypeBtnMap.get(type).setSelected(true);
//		} else {
//			algoMniGroup.clearSelection();
//		}
//		
//		Collection<KNetworkModule> modules = getNetwork().getNetModules();
//		for (KNetworkModule module : modules) {
//			KNode node = ((KNodeNetworkModule)module).getNode();
//			if (node instanceof KSensorNode) {
//				for (KSensor sensor : ((KSensorNode)node).getSensorList()) {
//					final String s = sensor.getSensorType();
//					if (!sensorTypeBtnMap.containsKey(s)) {
//						JRadioButtonMenuItem item = new JRadioButtonMenuItem(s); 
//						sensorTypeBtnMap.put(s, item);
//						mnSingleAbsolute.add(item);
//						algoMniGroup.add(item);
//						item.addActionListener(new ActionListener() {
//							@Override
//							public void actionPerformed(ActionEvent e) {
//								((KGateway)clickNode).setRequestAlgorithm("KNN-1:SingleSensorAbsolute-" + s);
//							}
//						});
//					}
//				}
//			}
//		}
//	}
	
	public void netViewMousePressed(MouseEvent evt) {
		if (evt.getButton() == MouseEvent.BUTTON1) {
			clickNode = getNodeAt(evt.getX(), evt.getY());
			if (clickNode != null)
		  		draggingNode = true;
		}
	}
	
	public void netViewMouseReleased(MouseEvent evt) {
		TPoint netLoc = toNetCoord(new TPoint(evt.getX(), evt.getY()));
		if (deployingNode) {
			if (deployNodeType.equalsIgnoreCase("sensorNode")) {
				//addSensorNode(deployingNodePath, netLoc); 
				
				//Get the actual Node
				KSensorNode sNode = addSensorNode(deployingNodePath, netLoc); // NEW
				
				/**
				 * Calculate the timeWindow for all new Nodes
				 * @author Danilo à Tellinghusen
				 */
				//Calculate the timeWindow and save it on the Node
				long timeWindow = TimeWindowAlgorithm.calculateTimeWindow(sNode);
				
				//ManualTimeWindow
				sNode.setTimeWindow(timeWindow);
				
				//CommonTimeWindow
				long currentCommonTimeWindow = simView.getSimulation().getDynamicTimeWindow();  //Get the current TW
				//Calculate the new TW in dependency of the current TW
				long newCommonTimeWindow = TimeWindowAlgorithm.calculateCommonTimeWindow(currentCommonTimeWindow,timeWindow);				
				 										
				simView.getSimulation().setDynamicTimeWindow(newCommonTimeWindow);
					KTunersFrame.refreshSlider(newCommonTimeWindow);
					
				/**
				 * End
				 */
			
			
			} else if (deployNodeType.equalsIgnoreCase("gateway")) {
				addGateway(netLoc);
			}
			deployingNode = false;
			deployingNodePath = null;
			repaint();
		}
		if (draggingNode && clickNode != null) {
			clickNode.setLocation(netLoc);
			draggingNode = false;
			clickNode = null;
			repaint();
		}
	}

	/**
	 * @param netLoc
	 */
	public KGateway addGateway(TPoint netLoc) {
		KGateway gateway = getSimulation().addGateway(netLoc);
		gateway.addStateChangeListener(this);
		return gateway;
	}

	/**
	 * @param netLoc
	 */
	public KSensorNode addSensorNode(String npfPath, TPoint netLoc) {
		KSensorNode node = getSimulation().addSensorNode(npfPath, netLoc);
		node.addStateChangeListener(this);
		return node;
	}

	public void netViewMouseDragged(MouseEvent evt) {
		if (clickNode != null) {
			Graphics gr = getGraphics();
			gr.setXORMode(Color.white);
			gr.setColor(Color.black);
			gr.drawLine(xM_Old-10, yM_Old, xM_Old+10, yM_Old);
			gr.drawLine(xM_Old, yM_Old-10, xM_Old, yM_Old+10);
			gr.drawLine(evt.getX()-10, evt.getY(), evt.getX()+10, evt.getY());
			gr.drawLine(evt.getX(), evt.getY()-10, evt.getX(), evt.getY()+10);
			xM_Old = evt.getX();
			yM_Old = evt.getY();
		}
	}
	
	public void netViewMouseMoved(MouseEvent evt) {
		if (deployingNode) {
			Graphics gr = this.getGraphics();
			gr.setXORMode(Color.white);
			gr.setColor(Color.black);
			gr.drawLine(xM_Old-10, yM_Old, xM_Old+10, yM_Old);
			gr.drawLine(xM_Old, yM_Old-10, xM_Old, yM_Old+10);
			gr.drawLine(evt.getX()-10, evt.getY(), evt.getX()+10, evt.getY());
			gr.drawLine(evt.getX(), evt.getY()-10, evt.getX(), evt.getY()+10);
			xM_Old = evt.getX();
			yM_Old = evt.getY();
		}
	}
	
	public void popNewSensorClick(ActionEvent evt) {
		JFileChooser fc = new JFileChooser();
		fc.setCurrentDirectory(new File("."));
		fc.setDialogTitle("Select sensor profile...");
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		String[] npf = new String[] { "npf"};
		fc.addChoosableFileFilter(new ExtFilter(npf,
				"Node profiles (*.npf)"));
		if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
			deployingNode = true;
			try {
				deployingNodePath = fc.getSelectedFile().getCanonicalPath();
			} catch (IOException e) {
				e.printStackTrace();
			}
			deployNodeType = "sensorNode";
		}
	}
	
	public void popNewGatewayClick(ActionEvent evt) {
		deployingNode = true;
		deployNodeType = "gateway";
	}
	
	public void popNewSceneClick(ActionEvent evt) {
		JFileChooser fc = new JFileChooser();
		fc.setMultiSelectionEnabled(true);
		fc.setCurrentDirectory(new File("."));
		fc.setDialogTitle("Select sensor profiles...");
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		String[] npf = new String[] { "npf"};
		fc.addChoosableFileFilter(new ExtFilter(npf,
				"Node profiles (*.npf)"));
		if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
			try {
				simView.resetSimulation();
				sim.addGateway(new TPoint(400,200));
				repaint();
				File[] files = fc.getSelectedFiles();
				Random rand = new Random();
				for (File file : files) {
					int x = rand.nextInt(getWidth() - 20) + 10;
					int y = rand.nextInt(getHeight() - 20) + 10;
					addSensorNode(file.getCanonicalPath(), new TPoint(x, y));
					repaint();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void popSaveSceneClick(ActionEvent evt) {
		JFileChooser fc = new JFileChooser();
		String scenarioName = null;
		fc.setCurrentDirectory(new File("."));
		fc.setDialogTitle("Enter scenario name to save...");
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		String[] sce = new String[] { "sce" };
		fc.addChoosableFileFilter(new ExtFilter(sce,
				"Sensor profiles (*.sce)"));
		if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
			try {
				scenarioName = fc.getSelectedFile().getCanonicalPath();
				if (!scenarioName.endsWith(".sce")) {
					scenarioName += ".sce";
				}
				saveScene(scenarioName);
			} catch (IOException e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(this, "Saving failed");
			}
		}		
	}

	/**
	 * @param scenarioName
	 * @throws IOException
	 */
	public void saveScene(String scenarioName) throws IOException {
		sim.saveScene(scenarioName);
	}

	public void popLoadSceneClick(ActionEvent evt) {
		JFileChooser fc = new JFileChooser();
		String scenarioName = null;
		fc.setCurrentDirectory(new File("."));
		fc.setDialogTitle("Select scenario to open");
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		String[] sce = new String[] { "sce" };
		fc.addChoosableFileFilter(new ExtFilter(sce,
				"KSim Scene files (*.sce)"));
		if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
			try {
				simView.resetSimulation();
				scenarioName = fc.getSelectedFile().getCanonicalPath();
				loadScene(scenarioName);
				repaint();
			} catch (IOException e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(this, "Loading failed");
			}
		}
	}

	/**
	 * @param scenarioName
	 * @throws IOException
	 */
	public void loadScene(String scenarioName) throws IOException {
		KNode[] nodes = sim.loadScene(scenarioName);
		for (KNode node : nodes) {
			node.addStateChangeListener(this);
		}
	}

	public void popChangeSDClick(ActionEvent evt) {
		KSensorNode s = (KSensorNode)clickNode;
		String st = JOptionPane.showInputDialog(this, "Please enter new semantic description:", "", JOptionPane.QUESTION_MESSAGE);
		if (st != null) {
			for (KSensor sensor : s.getSensorList()) {
				sensor.setDescription(st);
			}
		}
	}
	
	//do nothing
	public void popClearSDClick(ActionEvent evt) {
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
			for (KSensor sensor : sNode.getSensorList()) {
				sensor.setDescription("Unannotated");
			}
		}
	}
	
	public void popViewDataClick(ActionEvent evt) {
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
			final KNodeViewFrame frame = new KNodeViewFrame(
					sNode,sNode.getDisplayName(), true, true, true, true);
			// Add frame to desktop pane
			simView.add(frame);
			frame.add(new KNodePanel(sNode, frame));
			frame.pack();
			frame.setVisible(true);
			final KSimTimeElapseListener l = new KSimTimeElapseListener() {
				@Override
				

				public void timeElapsed() {
					frame.repaint();
				}
			};
			sim.getSimulator().addSimTimeElapseListener(l);
			frame.addInternalFrameListener(new InternalFrameAdapter() {
				@Override
				public void internalFrameClosed(InternalFrameEvent e) {
					sim.getSimulator().removeSimTimeElapseListener(l);
				}
			});
		} else {
			throw new RuntimeException("Not a sensorNode selected");
		}
	}
		protected void popViewRuleBaseClick(ActionEvent e) {
		if (clickNode instanceof KGateway) {
			final KGateway gateway = (KGateway) clickNode;
			final KNodeViewFrame frame = new KNodeViewFrame(
					gateway, "Rule base view", true, true, true, true);
			simView.add(frame);
			frame.add(new KFuzzyDBPanel(gateway.getFuzzyDb()));
			frame.pack();
			frame.setVisible(true);
		}
	}	

	//by Danilo - TimeWindow Options
	public void popCommonTimeWindowClick(ActionEvent evt) {
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
			sNode.setTimeWindowOption(1);
			System.out.println("Using common TimeWindow for Node " + sNode.getDisplayName());
			
			//Disable ManualTimeWindow Slider
			if (tunersFrame != null){
				tunersFrame.setVisible(false);
			}
		}
	}
	
	public void popManuallyTimeWindowClick(ActionEvent evt){
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
//			long currentTW = sNode.getTimeWindow();
							
					sNode.setTimeWindowOption(2);
					System.out.println("Using manually TimeWindow for Node " + sNode.getDisplayName());
										
					// Add frame to desktop pane
					final KNodeViewFrame frame = new KNodeViewFrame(
							sNode,sNode.getDisplayName(), true, true, true, true);

					// TunersFrame
					tunersFrame = new KTunersFrame(sNode);
					tunersFrame.addInternalFrameListener(new InternalFrameAdapter() {
						@Override
						public void internalFrameClosing(InternalFrameEvent e) {
							super.internalFrameClosing(e);
						
						}
					});
					simView.add(tunersFrame);
					tunersFrame.setVisible(true);

		}
	}
		

	public void popComputedTimeWindowClick(ActionEvent evt) {
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
			sNode.setTimeWindowOption(2);
			System.out.println("Using computed TimeWindow for Node " + sNode.getDisplayName());

			sNode.setTimeWindow(TimeWindowAlgorithm.calculateCumputedTimeWindow(sNode));
			
			//Disable ManualTimeWindow Slider
			if (tunersFrame != null){
				tunersFrame.setVisible(false);
			}
			
		}
	}
	
	
	public void popViewTimeWindowClick(ActionEvent evt) {
		if (clickNode instanceof KSensorNode) {
			final KSensorNode sNode = (KSensorNode) clickNode;
				if(sNode.getTimeWindowOption() == 1){
					System.out.println("TimeWindow from Node: " + sNode.getDisplayName() + " is " + KSensorNode.commonTimeWindow); }
				else{ System.out.println("TimeWindow from Node: " + sNode.getDisplayName() + " is " + sNode.getTimeWindow()); }
		}
	}
	//
	
	
	
	public void popDelNodeClick(ActionEvent evt) {
		KNode node = (KNode)clickNode;
		sim.removeNode(node);
		repaint();
	}
	
	public KNode getNodeAt(int simX, int simY) {
		KNode rs = null;
		TPoint pointLoc = new TPoint(simX, simY);
		for (KNetworkModule module : getSimulation().getNetwork().getNetModules()) {
			if (module instanceof KNodeNetworkModule) {
				KNode n = ((KNodeNetworkModule)module).getNode();
				TPoint viewLoc = toNetViewCoord(n.getLocation());
				int viewWidth = toNetViewSize(NODE_WIDTH);
				int viewHeight = toNetViewSize(NODE_HEIGHT);
				if (inRange(pointLoc, viewLoc, viewWidth, viewHeight)) {
					rs = n;
					break;
				}
			}
		}
		return rs;
	}
	
	private boolean inRange(TPoint pointLoc, TPoint viewLoc, int viewWidth,
			int viewHeight) {
		if ((pointLoc.x >= viewLoc.x - viewWidth/2)
				&& (pointLoc.x <= viewLoc.x + viewWidth/2)
				&& (pointLoc.y >= viewLoc.y - viewHeight/2)
				&& (pointLoc.y <= viewLoc.y + viewHeight/2)) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setPaintMode();
		drawLinks(g2d);
		g2d.setXORMode(Color.WHITE);
		for (KNetworkModule module : getSimulation().getNetwork().getNetModules()) {
			if (module instanceof KNodeNetworkModule) {
				paintNode(((KNodeNetworkModule)module).getNode(), g2d);
			}
		}
	}

	private void drawLinks(Graphics2D g) {
		g.setColor(Color.BLUE);
		for (KNetworkModule module1 : getSimulation().getNetwork().getNetModules()) {
			for (KNetworkModule module2 : getSimulation().getNetwork().getNetModules()) {
				if (module1 != module2 && module1.adjacentWith(module2)) {
					TPoint loc1 = ((KNodeNetworkModule)module1).getLocation();
					TPoint loc2 = ((KNodeNetworkModule)module2).getLocation();
					TPoint paintP1 = toNetViewCoord(loc1);
					TPoint paintP2 = toNetViewCoord(loc2);
					g.drawLine((int) paintP1.x, (int) paintP1.y,
							(int) paintP2.x, (int) paintP2.y);
				}
			}
		}
	}

	public void paintNode(KNode node, Graphics2D g) {
		TPoint netLoc = node.getLocation();
		TPoint paintLoc = toNetViewCoord(netLoc);
		int x = (int) paintLoc.x;
		int y = (int) paintLoc.y;
		int width = toNetViewSize(NODE_WIDTH);
		int height = toNetViewSize(NODE_HEIGHT);
		String label = node.getDisplayName();
		g.setFont(NODE_FONT);
		if (node instanceof KSensorNode) {
			g.setColor(SNODE_FILL_COLOR);
			g.fillOval(x - width / 2, y - height / 2, width, height);
			g.setColor(NODE_TEXT_COLOR);
		} else if (node instanceof KGateway) {
			int[] xpoints = new int[3];
			int[] ypoints = new int[3];
			xpoints[0] = x;
			ypoints[0] = y - height / 2;
			xpoints[1] = x - width / 2;
			ypoints[1] = y + height / 2;
			xpoints[2] = x + width / 2;
			ypoints[2] = y + height / 2;
			Polygon gwShape = new Polygon(xpoints, ypoints, 3);
			g.setColor(GTW_FILL_COLOR);
			g.fillPolygon(gwShape);
			g.setColor(NODE_TEXT_COLOR);
		}
		// label
		FontMetrics fm = getFontMetrics(NODE_FONT);
		int sw = fm.stringWidth(label);
		g.drawString(label, x - sw / 2, y - height / 2);
	}

	public TPoint toNetViewCoord(TPoint netLocation) {
		// TODO
		return netLocation;
	}

	public Dimension toNetViewSize(Dimension d) {
		// TODO
		return d;
	}

	public int toNetViewSize(int size) {
		// TODO
		return size;
	}
	
	private TPoint toNetCoord(TPoint viewLocation) {
		// TODO Auto-generated method stub
		return viewLocation;
	}

	public void setSimulation(KSimulation sim) {
		this.sim = sim;
	}

	public KSimulation getSimulation() {
		return sim;
	}
	
	public void setSimView(KSimulationView simView) {
		this.simView = simView;
	}

	public KSimulationView getSimView() {
		return simView;
	}

	public KNetwork getNetwork() {
		return sim.getNetwork();
	}
	
	public void destroy() {
		for (KNetworkModule module : sim.getNetwork().getNetModules()) {
			if (module instanceof KNodeNetworkModule) {
				KNodeNetworkModule nodeModule = (KNodeNetworkModule) module;
				nodeModule.getNode().removeStateChangeListener(this);
			}
		}
	}

	@Override
	public void stateChanged() {
		repaint();
	}
}
